﻿using System;
using System.Collections.Generic;
using System.Linq;
using Spanner;

namespace SpannerWebSite
{
    [SpannerPoco]
    public class Tile
    {
        public int Index;
        public ObsVar<string> Letter;
        public string Colour;
        public ObsVar<bool> Revealed;
        public bool Paired;
    }

    internal partial class MemoryGame : UI
    {
        internal class BodyHtmlAndInitAct {
            internal Html BodyHtml;
            internal Act InitAct;
        }

        internal static BodyHtmlAndInitAct Content()
        {
            const string word = "*SPANNER";

            const string wordword = word + word;

            var numTiles = wordword.Length;

            var sideLength = (int)Math.Sqrt(numTiles);

            var letters = Var<string[]>("letters", wordword.Select(x => x.ToString()).ToArray());

            var colours = Var<string[]>("colours", new string[] { "red", "green", "blue" });

            var numColours = Length(colours);

            var newTile = Function<string, int, Tile>("newTile", (letter, i) =>
                Let<Tile, Tile>(new Tile(), tile =>
                    ActExpr(
                        Do(
                            Set(tile.Index(), i),
                            Set(tile.Letter(), NewObsVar(letter)),
                            Set(tile.Colour(), Ith(colours, i % numColours)),
                            Set(tile.Revealed(), NewObsVar<bool>(true)),
                            Set(tile.Paired(), false)
                        ),
                        tile
                    )
                )
            );

            var tiles = Var<Tile[]>("tiles", Map(letters, newTile));

            var numClicks = ObsVar<int>("numClicks", 0);

            var newGame = Procedure("newGame", Do(
                ForEach(tiles, Proc<Tile, int>((tile, i) =>
                    Local(Floor(Random() * ToDouble(i + 1)), j =>
                    Local(Ith(tiles, i).Letter(), tmp =>
                        Do(
                            Set(Ith(tiles, i).Revealed(), false),
                            Set(Ith(tiles, i).Paired(), false),
                            Set(Ith(tiles, i).Letter(), Ith(tiles, j).Letter()),
                            Set(Ith(tiles, j).Letter(), tmp)
                        )
                    ))
                )),
                Set(numClicks, 0)
            ));

            var fst = Var<Tile>("fst", null);

            var snd = Var<Tile>("snd", null);

            var tileClicked = Procedure<Tile>("tileClicked", tile =>
                IfCase(
                    When(tile.Revealed() | tile.Paired(),
                        Noop()
                    ),
                    When(fst == null, Do(
                        Set(fst, tile),
                        Set(tile.Revealed(), true),
                        Set(numClicks, numClicks + 1)
                    )),
                    When(snd == null & tile.Letter() == fst.Letter(), Do(
                        Set(tile.Paired(), true),
                        Set(fst.Paired(), true),
                        Set(fst, null),
                        Set(tile.Revealed(), true),
                        Set(numClicks, numClicks + 1)
                    )),
                    When(snd == null, Do(
                        Set(snd, tile),
                        Set(tile.Revealed(), true),
                        Set(numClicks, numClicks + 1)
                    )),
                    When(snd != null, Do(
                        Set(fst.Revealed(), false),
                        Set(snd.Revealed(), false),
                        Set(fst, tile),
                        Set(snd, null),
                        Set(tile.Revealed(), true),
                        Set(numClicks, numClicks + 1)
                    ))
                )
            );

            var content = Group(
                Style(@"
                    div.tile {
                        width: 2em;
                        height: 1.2em;
                        float: left;
                        border: 2px solid green;
                        border-radius: 6px;
                        margin: 2px;
                        text-align: center;
                        color: white;
                        font: bold 32px/1.2 times;
                    }"),
                KoForEach(tiles, tile =>
                    Div(Show(tile.Letter()).KoVisible(tile.Revealed()))
                        .WithClass("tile")
                        .KoStyle(
                            CssStyleValue("background", tile.Colour()),
                            CssStyleValue("clear", Cond<string>(tile.Index() % sideLength == 0, "both", "none"))
                         )
                        .KoClick(Call(tileClicked, tile))
                ),
                Div(Show(numClicks), " click", Show(Cond<string>(numClicks == 1, "", "s")))
                    .WithAttr("style", "clear: both"),
                Button("New game").KoClick(newGame)
            );

            return new BodyHtmlAndInitAct { BodyHtml = content, InitAct = Call(newGame) };
        }

        internal static void WriteWebPage()
        {
            const string path = Const.DemoWebSitePath;
            var content = Content();
            var html = StandardSpannerHtmlPage("Memory game", content.BodyHtml);
            var webPage = WebPage("MemoryGame", html, content.InitAct);
            var ctxt = new Context(path);
            ctxt.WriteWebPage(webPage);
        }
    }
}
